"""Sensitive information filter for privacy protection"""

from typing import Dict, Any, Optional, List, Pattern
import re
import logging
from pathlib import Path

from .base_cleaner import BaseCleaner

logger = logging.getLogger(__name__)


class SensitiveInfoFilter(BaseCleaner):
    """Filter for detecting and masking sensitive information
    
    Handles phone numbers, ID cards, emails, and custom sensitive words
    """
    
    def __init__(self, config: Optional[Dict[str, Any]] = None):
        """Initialize sensitive info filter
        
        Args:
            config: Configuration with options:
                - mask_char: Character to use for masking (default: '*')
                - mask_partial: Whether to partially mask (default: True)
                - custom_words_file: Path to custom sensitive words file
                - detect_only: Only detect without masking (default: False)
        """
        super().__init__(config)
        self.mask_char = self.config.get('mask_char', '*')
        self.mask_partial = self.config.get('mask_partial', True)
        self.detect_only = self.config.get('detect_only', False)
        self.custom_words_file = self.config.get('custom_words_file')
        
        # Statistics
        self.stats = {
            'phone_numbers': 0,
            'id_cards': 0,
            'emails': 0,
            'custom_words': 0
        }
        
        # Compile regex patterns
        self._compile_patterns()
        
        # Load custom sensitive words
        self.custom_words = self._load_custom_words()
    
    def _compile_patterns(self):
        """Compile regex patterns for sensitive information"""
        # Chinese mobile phone number (11 digits, starting with 1)
        self.phone_pattern = re.compile(
            r'(?<![0-9])'  # Not preceded by digit
            r'1[3-9]\d{9}'  # 1 followed by 3-9, then 9 digits
            r'(?![0-9])',  # Not followed by digit
            re.IGNORECASE
        )
        
        # Chinese ID card (18 digits, may end with X)
        self.id_card_pattern = re.compile(
            r'(?<![0-9])'  # Not preceded by digit
            r'[1-9]\d{5}'  # Area code (6 digits)
            r'(?:19|20)\d{2}'  # Year (1900-2099)
            r'(?:0[1-9]|1[0-2])'  # Month
            r'(?:0[1-9]|[12]\d|3[01])'  # Day
            r'\d{3}[\dXx]'  # Sequence code and check digit
            r'(?![0-9])',  # Not followed by digit
            re.IGNORECASE
        )
        
        # Email address
        self.email_pattern = re.compile(
            r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
            re.IGNORECASE
        )
        
        # Bank card number (16-19 digits)
        self.bank_card_pattern = re.compile(
            r'(?<![0-9])'
            r'(?:'
            r'4\d{15}|'  # Visa
            r'5[1-5]\d{14}|'  # MasterCard
            r'3[47]\d{13}|'  # American Express
            r'6(?:011|5\d{2})\d{12}|'  # Discover
            r'62\d{14,17}'  # UnionPay
            r')'
            r'(?![0-9])',
            re.IGNORECASE
        )
        
        # QQ number (5-11 digits)
        self.qq_pattern = re.compile(
            r'(?:QQ|qq|Qq|扣扣)?[:：\s]*([1-9]\d{4,10})(?![0-9])',
            re.IGNORECASE
        )
        
        # WeChat ID
        self.wechat_pattern = re.compile(
            r'(?:微信|WeChat|wechat|wx|WX)[:：\s]*([a-zA-Z][\w-]{5,19})',
            re.IGNORECASE
        )
    
    def _load_custom_words(self) -> List[str]:
        """Load custom sensitive words from file
        
        Returns:
            List of sensitive words
        """
        words = []
        
        if self.custom_words_file:
            path = Path(self.custom_words_file)
            if path.exists():
                try:
                    with open(path, 'r', encoding='utf-8') as f:
                        words = [line.strip() for line in f if line.strip()]
                    logger.info(f"Loaded {len(words)} custom sensitive words")
                except Exception as e:
                    logger.error(f"Failed to load custom words: {e}")
        
        return words
    
    def clean(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """Filter sensitive information from data
        
        Args:
            data: Input data dictionary
            
        Returns:
            Data with filtered sensitive information
        """
        # Track if sensitive info was found
        has_sensitive = False
        
        # Process text fields
        text_fields = ['title', 'description', 'excerpt']
        for field in text_fields:
            if field in data and data[field]:
                data[field], found = self._filter_text(data[field])
                has_sensitive = has_sensitive or found
        
        # Process content
        if 'content' in data and data['content']:
            if isinstance(data['content'], str):
                data['content'], found = self._filter_text(data['content'])
                has_sensitive = has_sensitive or found
            elif isinstance(data['content'], dict):
                for key, value in data['content'].items():
                    if isinstance(value, str):
                        data['content'][key], found = self._filter_text(value)
                        has_sensitive = has_sensitive or found
                    elif isinstance(value, list):
                        # Handle comments or other list fields
                        filtered_list = []
                        for item in value:
                            if isinstance(item, str):
                                filtered_item, found = self._filter_text(item)
                                filtered_list.append(filtered_item)
                                has_sensitive = has_sensitive or found
                            else:
                                filtered_list.append(item)
                        data['content'][key] = filtered_list
        
        # Add metadata about sensitive info detection
        if 'cleaned_data' not in data:
            data['cleaned_data'] = {}
        data['cleaned_data']['has_sensitive'] = has_sensitive
        data['cleaned_data']['sensitive_stats'] = self.stats.copy()
        
        return data
    
    def _filter_text(self, text: str) -> tuple[str, bool]:
        """Filter sensitive information from text
        
        Args:
            text: Text to filter
            
        Returns:
            Tuple of (filtered text, whether sensitive info was found)
        """
        if not text:
            return text, False
        
        has_sensitive = False
        
        # Filter phone numbers
        if self.phone_pattern.search(text):
            has_sensitive = True
            self.stats['phone_numbers'] += len(self.phone_pattern.findall(text))
            if not self.detect_only:
                text = self.phone_pattern.sub(
                    lambda m: self._mask_value(m.group(), 'phone'),
                    text
                )
        
        # Filter ID cards
        if self.id_card_pattern.search(text):
            has_sensitive = True
            self.stats['id_cards'] += len(self.id_card_pattern.findall(text))
            if not self.detect_only:
                text = self.id_card_pattern.sub(
                    lambda m: self._mask_value(m.group(), 'id_card'),
                    text
                )
        
        # Filter emails
        if self.email_pattern.search(text):
            has_sensitive = True
            self.stats['emails'] += len(self.email_pattern.findall(text))
            if not self.detect_only:
                text = self.email_pattern.sub(
                    lambda m: self._mask_value(m.group(), 'email'),
                    text
                )
        
        # Filter bank cards
        if self.bank_card_pattern.search(text):
            has_sensitive = True
            if not self.detect_only:
                text = self.bank_card_pattern.sub(
                    lambda m: self._mask_value(m.group(), 'bank_card'),
                    text
                )
        
        # Filter QQ numbers
        qq_matches = self.qq_pattern.findall(text)
        if qq_matches:
            has_sensitive = True
            if not self.detect_only:
                for match in qq_matches:
                    text = text.replace(match, self._mask_value(match, 'qq'))
        
        # Filter custom sensitive words
        if self.custom_words and not self.detect_only:
            for word in self.custom_words:
                if word in text:
                    has_sensitive = True
                    self.stats['custom_words'] += text.count(word)
                    text = text.replace(word, self.mask_char * len(word))
        
        return text, has_sensitive
    
    def _mask_value(self, value: str, value_type: str) -> str:
        """Mask a sensitive value
        
        Args:
            value: Value to mask
            value_type: Type of value (phone, email, etc.)
            
        Returns:
            Masked value
        """
        if not self.mask_partial:
            return self.mask_char * len(value)
        
        # Partial masking strategies
        if value_type == 'phone':
            # Show first 3 and last 4 digits
            if len(value) == 11:
                return value[:3] + self.mask_char * 4 + value[-4:]
            
        elif value_type == 'id_card':
            # Show first 6 and last 4
            if len(value) == 18:
                return value[:6] + self.mask_char * 8 + value[-4:]
            
        elif value_type == 'email':
            # Mask part of username and domain
            parts = value.split('@')
            if len(parts) == 2:
                username = parts[0]
                domain = parts[1]
                if len(username) > 2:
                    username = username[0] + self.mask_char * (len(username) - 2) + username[-1]
                else:
                    username = self.mask_char * len(username)
                return f"{username}@{domain}"
        
        elif value_type == 'bank_card':
            # Show first 4 and last 4 digits
            if len(value) >= 8:
                return value[:4] + self.mask_char * (len(value) - 8) + value[-4:]
        
        elif value_type == 'qq':
            # Mask middle part
            if len(value) > 4:
                return value[:2] + self.mask_char * (len(value) - 4) + value[-2:]
        
        # Default: mask entire value
        return self.mask_char * len(value)
    
    def get_stats(self) -> Dict[str, Any]:
        """Get filter statistics
        
        Returns:
            Statistics about filtered sensitive information
        """
        stats = super().get_stats()
        stats['filtered'] = self.stats.copy()
        return stats